var lib = Base.extend({
	constructor: null,
	// sprintf() implementation for javascript
	// wonghang: I copied it from http://jan.moesen.nu/code/javascript/sprintf-and-printf-in-javascript/,
	//           the page said it is in Public Domain, so it is free to use
	sprintf: function () 
	{
		if (!arguments || arguments.length < 1 || !RegExp)
		{
			return;
		}
		var str = arguments[0];
		var re = /([^%]*)%('.|0|\x20)?(-)?(\d+)?(\.\d+)?(%|b|c|d|u|f|o|s|x|X)(.*)/;
		var a = b = [], numSubstitutions = 0, numMatches = 0;
		while (a = re.exec(str))
		{
			var leftpart = a[1], pPad = a[2], pJustify = a[3], pMinLength = a[4];
			var pPrecision = a[5], pType = a[6], rightPart = a[7];
			
			//alert(a + '\n' + [a[0], leftpart, pPad, pJustify, pMinLength, pPrecision);
	
			numMatches++;
			if (pType == '%')
			{
				subst = '%';
			}
			else
			{
				numSubstitutions++;
				if (numSubstitutions >= arguments.length)
				{
					Debug.trace(0,'lib.sprintf: Error! Not enough function arguments (' + (arguments.length - 1) + ', excluding the string)\nfor the number of substitution parameters in string (' + numSubstitutions + ' so far).');
				}
				var param = arguments[numSubstitutions];
				var pad = '';
					   if (pPad && pPad.substr(0,1) == "'") pad = leftpart.substr(1,1);
				  else if (pPad) pad = pPad;
				var justifyRight = true;
					   if (pJustify && pJustify === "-") justifyRight = false;
				var minLength = -1;
					   if (pMinLength) minLength = parseInt(pMinLength);
				var precision = -1;
					   if (pPrecision && pType == 'f') precision = parseInt(pPrecision.substring(1));
				var subst = param;
					   if (pType == 'b') subst = parseInt(param).toString(2);
				  else if (pType == 'c') subst = String.fromCharCode(parseInt(param));
				  else if (pType == 'd') subst = parseInt(param) ? parseInt(param) : 0;
				  else if (pType == 'u') subst = Math.abs(param);
				  else if (pType == 'f') subst = (precision > -1) ? Math.round(parseFloat(param) * Math.pow(10, precision)) / Math.pow(10, precision): parseFloat(param);
				  else if (pType == 'o') subst = parseInt(param).toString(8);
				  else if (pType == 's') subst = param;
				  else if (pType == 'x') subst = ('' + parseInt(param).toString(16)).toLowerCase();
				  else if (pType == 'X') subst = ('' + parseInt(param).toString(16)).toUpperCase();
			}
			str = leftpart + subst + rightPart;
		}
		return str;
	},
	AppendHead: function(node)
	{
		var head;

		head = document.getElementsByTagName('head');
		head = head.item(0);

		head.appendChild(node);
		return true;
	},
	LoadCSSFile: function(file)
	{
		var link = document.createElement('link');

		link.href = file;
		link.rel = 'stylesheet';
		link.type = 'text/css';

		Debug.trace(0,"lib.LoadCSSFile: Load a CSS from <a href=\"%s\">%s</a>",link.href,link.href);
		return this.AppendHead(link);
	},
	RunScript: function(s)
	{
		if(window.execScript) window.execScript(s);
		else window.eval(s);
	},
	GetEventTarget: function(e)
	{
		var t;
		
		if(e.target) t = e.target;
		else if(e.srcElement) t = e.srcElement;
		//if(t.nodeType == 3) t = t.parentNode;// for Safari bug (I search in Google)
		return t;
	},
	isUndefined: function(obj)
	{
		return (typeof(obj) == 'undefined') ? true : false;
	},
	time: function()
	{
		var a = new Date;
		return a.getTime();
	},
	/*  
	*  (c) 2006 Michael Porter
	*/
	InvalidUtf8Exception: function () {},
	utf8str: function(codes) 
	{
		var codes = [].concat(codes);
		codes.reverse();
		var getCode = function () {
			if(codes.length == 0) throw new this.InvalidUtf8Exception;
			return codes.pop();
		}
		var result = [];
		try {
			while (codes.length > 0) {
				var code0 = getCode();
				if (code0 < 0x80) result.push(code0);
				else {
					var code1 = getCode();
					if (code0 < 0xE0) result.push(((code0 & 0x1F) << 6) | (code1 & 0x3F));
					else {
						var code2 = getCode();
						if (code0 < 0xF0) result.push(((code0 & 0xF) << 12) | ((code1 & 0x3F) << 6) | (code2 & 0x3F));
						else {
							var code3 = getCode();
							if (code0 < 0xF8) result.push(((code0 & 0x7) << 18) | ((code1 & 0x3F) << 12) | ((code2 & 0x3F) << 6) | (code3 & 0x3F));
							else throw new this.InvalidUtf8Exception();
						}
					}
				}
			}
		}
		catch (e) {
			if (e instanceof this.InvalidUtf8Exception) result = [];
			else throw e;
		}
		return this.utf16str(result);
	},
	utf16str: function(codes) 
	{
		var result = [];
		for (var i = 0; i < codes.length; i += 1) {
			var code = codes[i];
			if (code <= 0xFFFF) result.push(String.fromCharCode(code));
			else if (code <= 0x10FFFF) {
				result.push(String.fromCharCode(
//					0xD800 | (((code >>> 16) & 0x1F) - 1) | ((code >>> 10) & 0x3F),
					0xD800 | (((code - 0x10000) >>> 10) & 0x3FF),
					0xDC00 | (code & 0x3FF)
				));
			}
			else return [];
		}
		return result.join("");
	},
	utf16char: function(code)
	{
		var result = [];
		if (code <= 0xFFFF) result.push(String.fromCharCode(code));
		else if (code <= 0x10FFFF) {
			result.push(String.fromCharCode(
//				0xD800 | (((code >>> 16) & 0x1F) - 1) | ((code >>> 10) & 0x3F),
				0xD800 | (((code - 0x10000) >>> 10) & 0x3FF),
				0xDC00 | (code & 0x3FF)
			));
		}
		return result.join("");
	},
	chartoutf16: function(ch)
	{
		var ch1 = ch.charCodeAt(0);
		if(ch1 < 0xD800 || ch1 > 0xDFFF) return ch1;
		else {
			var ch2 = ch.charCodeAt(1);
			return ((((ch2 >>> 6) & 0xF) + 1) << 16) | (ch1 & 0x3F) << 10 | (ch2 & 0x3FF);
		}
	},
	utf16charlen: function(ch)
	{
		var c = ch.charCodeAt(0);
		if(c < 0xD800 || c > 0xDFFF) return 1;
		else return 2;
	}
});
